import { Types } from 'mongoose';
import NodeCache from 'node-cache';
import crypto from 'crypto';
import { logger } from '@utils/logger';
import { ApiError } from '@middleware/error';

interface LearningData {
  userId: Types.ObjectId;
  courseId: Types.ObjectId;
  assessments: Array<{
    date: Date;
    score: number;
    type: string;
  }>;
  activities: Array<{
    date: Date;
    duration: number;
    type: string;
  }>;
}

interface PredictionResult {
  progressRate: number; // 学习进度百分比
  estimatedCompletion: Date; // 预计完成时间
  riskLevel: 'low' | 'medium' | 'high'; // 学习风险等级
  riskFactors: string[]; // 风险因素
  recommendations: string[]; // 改进建议
  performanceMetrics: {
    currentPerformance: number; // 当前表现分数
    trend: 'improving' | 'stable' | 'declining'; // 学习趋势
    consistencyScore: number; // 学习稳定性分数
    engagementLevel: number; // 参与度分数
  };
}

class PredictionService {
  private cache: NodeCache;
  private readonly RISK_THRESHOLD = {
    LOW: 0.3,
    MEDIUM: 0.6,
    HIGH: 0.8
  };
  private readonly MAX_RETRIES = 3;

  constructor() {
    // 设置缓存，TTL为12小时，每6小时检查过期
    this.cache = new NodeCache({ 
      stdTTL: 43200,
      checkperiod: 21600,
      useClones: false
    });

    // 设置缓存错误处理
    this.cache.on('error', (err) => {
      logger.error('Prediction cache error:', err);
    });
  }

  /**
   * 预测学习进度和风险
   */
  async predictProgress(data: LearningData): Promise<PredictionResult> {
    try {
      // 验证输入数据
      this.validateInput(data);

      // 检查缓存
      const cacheKey = this.generateCacheKey(data);
      const cachedResult = this.cache.get<PredictionResult>(cacheKey);
      if (cachedResult) {
        logger.info('Cache hit for prediction result');
        return cachedResult;
      }

      let retries = 0;
      while (retries < this.MAX_RETRIES) {
        try {
          // 计算主要指标
          const performanceMetrics = this.calculatePerformanceMetrics(data);
          const progressRate = this.calculateProgressRate(data);
          const estimatedCompletion = this.estimateCompletionDate(data, progressRate);
          const riskAnalysis = this.analyzeRisk(data, performanceMetrics);

          const result: PredictionResult = {
            progressRate,
            estimatedCompletion,
            riskLevel: riskAnalysis.level,
            riskFactors: riskAnalysis.factors,
            recommendations: this.generateRecommendations(riskAnalysis, performanceMetrics),
            performanceMetrics
          };

          // 缓存结果
          this.cache.set(cacheKey, result);
          logger.info('Prediction result cached');

          return result;
        } catch (error) {
          retries++;
          if (retries === this.MAX_RETRIES) {
            throw error;
          }
          logger.warn(`Retrying prediction, attempt ${retries}`);
        }
      }

      throw new ApiError(500, 'PREDICTION_ERROR', '预测失败超过最大重试次数');
    } catch (error) {
      logger.error('Prediction error:', error);
      throw error instanceof ApiError ? error : new ApiError(500, 'PREDICTION_ERROR', '预测过程发生错误');
    }
  }

  /**
   * 验证输入数据
   */
  private validateInput(data: LearningData): void {
    if (!data.assessments || !data.activities) {
      throw new ApiError(400, 'INVALID_INPUT', '评估记录和活动记录不能为空');
    }

    // 验证评估记录
    data.assessments.forEach(assessment => {
      if (assessment.score < 0 || assessment.score > 100) {
        throw new ApiError(400, 'INVALID_INPUT', '评估分数必须在0-100之间');
      }
      if (!assessment.type) {
        throw new ApiError(400, 'INVALID_INPUT', '评估类型不能为空');
      }
    });

    // 验证活动记录
    data.activities.forEach(activity => {
      if (activity.duration < 0) {
        throw new ApiError(400, 'INVALID_INPUT', '活动时长不能为负数');
      }
      if (!activity.type) {
        throw new ApiError(400, 'INVALID_INPUT', '活动类型不能为空');
      }
    });
  }

  /**
   * 计算性能指标
   */
  private calculatePerformanceMetrics(data: LearningData) {
    try {
      const assessments = data.assessments;
      const activities = data.activities;

      // 计算当前表现
      const currentPerformance = this.calculateCurrentPerformance(assessments);
      // 分析学习趋势
      const trend = this.analyzeTrend(assessments);
      // 计算学习稳定性
      const consistencyScore = this.calculateConsistency(activities);
      // 计算参与度
      const engagementLevel = this.calculateEngagement(activities);

      return {
        currentPerformance,
        trend,
        consistencyScore,
        engagementLevel
      };
    } catch (error) {
      logger.error('Performance metrics calculation error:', error);
      throw new ApiError(500, 'CALCULATION_ERROR', '性能指标计算失败');
    }
  }

  /**
   * 分析学习风险
   */
  private analyzeRisk(data: LearningData, metrics: PredictionResult['performanceMetrics']) {
    const riskFactors: string[] = [];
    let riskScore = 0;

    // 评估表现风险
    if (metrics.currentPerformance < 70) {
      riskFactors.push('当前学习表现不佳');
      riskScore += 0.3;
    }

    // 评估趋势风险
    if (metrics.trend === 'declining') {
      riskFactors.push('学习成绩呈下降趋势');
      riskScore += 0.2;
    }

    // 评估参与度风险
    if (metrics.engagementLevel < 0.6) {
      riskFactors.push('课程参与度较低');
      riskScore += 0.25;
    }

    // 评估稳定性风险
    if (metrics.consistencyScore < 0.5) {
      riskFactors.push('学习不够稳定');
      riskScore += 0.25;
    }

    // 评估时间投入风险
    const averageTimePerDay = this.calculateAverageTimePerDay(data.activities);
    if (averageTimePerDay < 60) { // 少于1小时/天
      riskFactors.push('每日学习时间不足');
      riskScore += 0.2;
    }

    // 确定风险等级
    let level: PredictionResult['riskLevel'];
    if (riskScore >= this.RISK_THRESHOLD.HIGH) {
      level = 'high';
    } else if (riskScore >= this.RISK_THRESHOLD.MEDIUM) {
      level = 'medium';
    } else {
      level = 'low';
    }

    return { level, factors: riskFactors, score: riskScore };
  }

  /**
   * 生成改进建议
   */
  private generateRecommendations(
    riskAnalysis: { level: PredictionResult['riskLevel']; factors: string[]; score: number },
    metrics: PredictionResult['performanceMetrics']
  ): string[] {
    const recommendations = new Set<string>();

    // 基于风险等级的建议
    if (riskAnalysis.level === 'high') {
      recommendations.add('建议立即与导师预约辅导');
      recommendations.add('制定详细的学习计划和目标');
    }

    // 基于具体风险因素的建议
    riskAnalysis.factors.forEach(factor => {
      if (factor.includes('表现不佳')) {
        recommendations.add('复习关键知识点');
        recommendations.add('参加补充课程或辅导');
      }
      if (factor.includes('下降趋势')) {
        recommendations.add('分析成绩下降原因');
        recommendations.add('调整学习方法和策略');
      }
      if (factor.includes('参与度')) {
        recommendations.add('增加课程参与度');
        recommendations.add('主动参与课堂讨论和实践');
      }
      if (factor.includes('不够稳定')) {
        recommendations.add('建立规律的学习习惯');
        recommendations.add('使用时间管理工具');
      }
      if (factor.includes('时间不足')) {
        recommendations.add('合理规划每日学习时间');
        recommendations.add('提高学习效率');
      }
    });

    return Array.from(recommendations);
  }

  /**
   * 计算当前学习进度
   */
  private calculateProgressRate(data: LearningData): number {
    const totalActivities = data.activities.length;
    if (totalActivities === 0) return 0;

    const completedActivities = data.activities.filter(
      activity => activity.duration > 0
    ).length;

    const progressRate = (completedActivities / totalActivities) * 100;
    return Math.round(progressRate * 10) / 10; // 保留一位小数
  }

  /**
   * 预估完成时间
   */
  private estimateCompletionDate(data: LearningData, progressRate: number): Date {
    const now = new Date();
    const averageProgressPerDay = this.calculateAverageProgressPerDay(data);
    const remainingProgress = 100 - progressRate;
    const remainingDays = remainingProgress / averageProgressPerDay;

    // 添加一些随机波动，使预测更真实
    const randomAdjustment = Math.random() * 2 - 1; // -1到1之间的随机数
    const adjustedDays = Math.max(1, remainingDays + randomAdjustment);

    return new Date(now.getTime() + adjustedDays * 24 * 60 * 60 * 1000);
  }

  private generateCacheKey(data: LearningData): string {
    const content = `${data.userId}_${data.courseId}_${data.assessments.length}_${data.activities.length}`;
    return `prediction_${crypto.createHash('md5').update(content).digest('hex')}`;
  }

  private calculateCurrentPerformance(assessments: LearningData['assessments']): number {
    if (assessments.length === 0) return 0;

    // 使用最近的评估记录，并给予较新的记录更高的权重
    const recentAssessments = assessments
      .sort((a, b) => b.date.getTime() - a.date.getTime())
      .slice(0, 3);

    const totalWeight = 6; // 1 + 2 + 3
    let weightedSum = 0;

    recentAssessments.forEach((assessment, index) => {
      const weight = 3 - index; // 最近的记录权重为3，次近为2，最远为1
      weightedSum += assessment.score * weight;
    });

    return Math.round((weightedSum / totalWeight) * 10) / 10;
  }

  private analyzeTrend(
    assessments: LearningData['assessments']
  ): 'improving' | 'stable' | 'declining' {
    if (assessments.length < 2) return 'stable';

    const sortedAssessments = assessments
      .sort((a, b) => a.date.getTime() - b.date.getTime())
      .map(a => a.score);
    
    const trend = this.calculateTrendSlope(sortedAssessments);

    if (trend > 0.1) return 'improving';
    if (trend < -0.1) return 'declining';
    return 'stable';
  }

  private calculateConsistency(activities: LearningData['activities']): number {
    if (activities.length === 0) return 0;

    const intervals = activities
      .sort((a, b) => a.date.getTime() - b.date.getTime())
      .map((activity, index, array) => {
        if (index === 0) return 0;
        return activity.date.getTime() - array[index - 1].date.getTime();
      })
      .slice(1);

    const meanInterval = intervals.reduce((sum, interval) => sum + interval, 0) / intervals.length;
    const variance = intervals.reduce((sum, interval) => sum + Math.pow(interval - meanInterval, 2), 0) / intervals.length;
    const standardDeviation = Math.sqrt(variance);

    return Math.round((1 / (1 + standardDeviation / meanInterval)) * 100) / 100;
  }

  private calculateEngagement(activities: LearningData['activities']): number {
    if (activities.length === 0) return 0;

    const totalDuration = activities.reduce((sum, activity) => sum + activity.duration, 0);
    const averageDuration = totalDuration / activities.length;

    // 假设3小时为满分，计算参与度得分
    return Math.round(Math.min(averageDuration / (3 * 60), 1) * 100) / 100;
  }

  private calculateAverageProgressPerDay(data: LearningData): number {
    if (data.activities.length < 2) return 5; // 默认每天5%的进度

    const activities = data.activities.sort(
      (a, b) => a.date.getTime() - b.date.getTime()
    );
    const daysPassed = Math.max(1,
      (activities[activities.length - 1].date.getTime() -
        activities[0].date.getTime()) /
      (24 * 60 * 60 * 1000)
    );

    return Math.round((data.activities.length / daysPassed) * 10 * 10) / 10;
  }

  private calculateAverageTimePerDay(activities: LearningData['activities']): number {
    if (activities.length === 0) return 0;

    const totalDuration = activities.reduce((sum, activity) => sum + activity.duration, 0);
    const uniqueDates = new Set(
      activities.map(activity => activity.date.toISOString().split('T')[0])
    ).size;

    return totalDuration / uniqueDates;
  }

  private calculateTrendSlope(scores: number[]): number {
    const n = scores.length;
    if (n < 2) return 0;

    const indices = Array.from({ length: n }, (_, i) => i);
    const meanX = indices.reduce((sum, x) => sum + x, 0) / n;
    const meanY = scores.reduce((sum, y) => sum + y, 0) / n;
    
    const numerator = indices.reduce((sum, x, i) => sum + (x - meanX) * (scores[i] - meanY), 0);
    const denominator = indices.reduce((sum, x) => sum + Math.pow(x - meanX, 2), 0);
    
    return denominator === 0 ? 0 : Math.round(numerator / denominator * 100) / 100;
  }

  /**
   * 清理缓存
   */
  public clearCache(): void {
    const stats = this.cache.getStats();
    this.cache.flushAll();
    logger.info(`Prediction cache cleared: ${stats.keys} keys removed`);
  }
}

export const predictionService = new PredictionService();
